package top.goldenyear.porpoise.infra.log.interceptor;

import top.goldenyear.porpoise.framework.common.util.monitor.TracerUtils;
import top.goldenyear.porpoise.framework.security.core.util.SecurityUtils;
import java.time.LocalDateTime;
import lombok.extern.slf4j.Slf4j;
import org.noear.solon.annotation.Component;
import org.noear.solon.annotation.Inject;
import org.noear.solon.core.handle.*;
import org.noear.solon.core.route.RouterInterceptor;
import org.noear.solon.core.route.RouterInterceptorChain;
import org.noear.solon.core.wrap.MethodWrap;
import top.goldenyear.porpoise.infra.log.annotations.AuditLog;
import top.goldenyear.porpoise.infra.log.model.OperateLog;
import top.goldenyear.porpoise.infra.log.service.OperateLogService;
import top.goldenyear.porpoise.infra.log.util.OperateLogUtils;

/**
 *
 * @author airhead
 */
@Component
@Slf4j
public class AuditLogInterceptor implements RouterInterceptor {
  @Inject private OperateLogService operateLogService;

  /**
   * 只记录有注解的日志
   *
   * @param ctx
   * @param auditLog
   * @return
   */
  private static boolean isLogEnable(Context ctx, AuditLog auditLog) {
    // 有 @AuditLog 注解的情况下
    if (auditLog != null) {
      return auditLog.enable();
    }

    return false;
  }

  private static AuditLog getAuditLog(Context ctx) {
    Action action = ctx.action();
    MethodWrap method = action.method();
    return method.getAnnotation(AuditLog.class);
  }

  private static void fillUserFields(OperateLog operateLogObj) {
    operateLogObj.setUserId(SecurityUtils.getLoginUserId());
    operateLogObj.setUserType(SecurityUtils.getLoginUserType());
  }

  @SuppressWarnings("unchecked")
  private static void fillModuleFields(Context ctx, AuditLog auditLog, OperateLog operateLog) {
    // module 属性
    if (auditLog != null) {
      operateLog.setModule(auditLog.module());
      operateLog.setName(auditLog.name());
    }

    // content 和 exts 属性
    operateLog.setContent(OperateLogUtils.getContent());
    operateLog.setExts(OperateLogUtils.getExt());
  }

  private static void fillRequestFields(Context ctx, OperateLog operateLog) {
    // 补全请求信息
    operateLog.setRequestMethod(ctx.method());
    operateLog.setRequestUrl(ctx.url());
    operateLog.setUserIp(ctx.realIp());
    operateLog.setUserAgent(ctx.userAgent());
  }

  private static void fillMethodFields(Context ctx, AuditLog auditLog, OperateLog operateLog) {
    if (auditLog != null) {
      operateLog.setType(auditLog.type().getType());
    }
  }

  @Override
  public void doIntercept(Context ctx, Handler mainHandler, RouterInterceptorChain chain)
      throws Throwable {
    chain.doIntercept(ctx, mainHandler);
  }

  @Override
  public Object postResult(Context ctx, Object result) throws Throwable {
    AuditLog auditLog = getAuditLog(ctx);
    if (isLogEnable(ctx, auditLog)) {
      OperateLog operateLog = buildLog(ctx, auditLog, result);
      operateLogService.log(operateLog);
    }

    return result;
  }

  private OperateLog buildLog(Context ctx, AuditLog auditLog, Object result) {
    OperateLog operateLog = new OperateLog();
    // 补全通用字段
    operateLog.setTraceId(TracerUtils.getTraceId());
    operateLog.setStartTime(LocalDateTime.now());

    // 补全请求信息
    fillRequestFields(ctx, operateLog);
    // 补充用户信息
    fillUserFields(operateLog);
    // 补全模块信息
    fillModuleFields(ctx, auditLog, operateLog);
    // 补全方法信息
    fillMethodFields(ctx, auditLog, operateLog);

    return operateLog;
  }
}
